/******************************************************************************
* Copyright (C) 2018 - 2022 Xilinx, Inc.  All rights reserved.
* Copyright (c) 2023 Advanced Micro Devices, Inc. All Rights Reserved.
* SPDX-License-Identifier: MIT
 ******************************************************************************/

/*****************************************************************************/
/**
 *
 * @file xusb_class_audio_dfu.c
 *
 * This file contains the implementation of the AUDIO-DFU composite device
 * specific class code for the example.
 *
 *<pre>
 * MODIFICATION HISTORY:
 *
 * Ver   Who  Date     Changes
 * ----- ---- -------- -------------------------------------------------------
 * 1.0   rb   22/02/18 First release
 * 1.5   vak  13/02/19 Added support for versal
 * 1.5   vak  03/25/19 Fixed incorrect data_alignment pragma directive for IAR
 *
 *</pre>
 ******************************************************************************/

/***************************** Include Files *********************************/
#include "xparameters.h"	/* XPAR parameters */
#include "xusb_ch9_audio_dfu.h"
#include "xusb_class_audio_dfu.h"

/************************** Constant Definitions *****************************/

/***************** Macros (Inline Functions) Definitions *********************/

static inline void Usb_DfuWaitForReset(struct dfu_if *DFU)
{
	/* This bit would be cleared when reset happens*/
	DFU->dfu_wait_for_interrupt = 1;
	dmb();
	while (DFU->dfu_wait_for_interrupt == 0) {
		/* Wait for an reset event */
		;
	}
}

/**************************** Type Definitions *******************************/

/************************** Function Prototypes ******************************/

/************************** Variable Definitions *****************************/
u8 DetachCounter = 0;

/*****************************************************************************/
/**
* This function handles setting of DFU state.
*
* @param	DFU is a pointer to DFU instance of the controller
* @param	dfu_state is a value of the DFU state to be set
*
* @return	- XST_SUCCESS if the function is successful.
*		- XST_FAILURE if an Error occurred.
*
* @note		None.
*
******************************************************************************/
s32 Usb_DfuSetState(struct dfu_if *DFU, u8 dfu_state)
{
	switch (dfu_state) {

		case STATE_APP_IDLE:
			DFU->curr_state = STATE_APP_IDLE;
			DFU->next_state = STATE_APP_DETACH;
			DFU->status = DFU_STATUS_OK;
			/* Set to runtime mode by default*/
			DFU->is_dfu = 0;
			DFU->runtime_to_dfu = 0;
			break;

		case STATE_APP_DETACH:
			if (DFU->curr_state == STATE_APP_IDLE) {
				DFU->curr_state = STATE_APP_DETACH;
				DFU->next_state = STATE_DFU_IDLE;

#ifdef DFU_DEBUG
				xil_printf("Waiting for USB reset to happen"
					   "to enter into DFU_IDLE state....\n");
#endif

				/* Wait For USB Reset to happen */
				Usb_DfuWaitForReset(DFU);

#ifdef DFU_DEBUG
				xil_printf("Got reset, entering DFU_IDLE state\n");
#endif
				/* Setting DFU mode */
				DFU->is_dfu = 1;

				/* Set this flag to indicate we are
				 * going from runtime to dfu mode
				 */
				DFU->runtime_to_dfu = 1;

				/* fall through */
			} else if (DFU->curr_state == STATE_DFU_IDLE) {
#ifdef DFU_DEBUG
				xil_printf("Waiting for USB reset to"
					   "happen to enter into APP_IDLE state....\n");
#endif
				/* Wait For USB Reset to happen */
				Usb_DfuWaitForReset(DFU);

				DFU->curr_state = STATE_APP_IDLE;
				DFU->next_state = STATE_APP_DETACH;
				DFU->status = DFU_STATUS_OK;
				DFU->is_dfu = 0;
				break;

			} else {
				goto stall;
			}

		case STATE_DFU_IDLE:
			DFU->curr_state = STATE_DFU_IDLE;
			DFU->next_state = STATE_DFU_DOWNLOAD_SYNC;
			DFU->is_dfu = 1;
			break;

		case STATE_DFU_DOWNLOAD_SYNC:
			DFU->curr_state = STATE_DFU_DOWNLOAD_SYNC;
			break;

		case STATE_DFU_DOWNLOAD_BUSY:
		case STATE_DFU_DOWNLOAD_IDLE:
		case STATE_DFU_MANIFEST_SYNC:
		case STATE_DFU_MANIFEST:
		case STATE_DFU_MANIFEST_WAIT_RESET:
		case STATE_DFU_UPLOAD_IDLE:
		case STATE_DFU_ERROR:
		default:
stall:
			/* Unsupported command. Stall the end point */
			DFU->curr_state = STATE_DFU_ERROR;
			EpSetStall(DFU->InstancePtr->PrivateData, 0, USB_EP_DIR_IN);
			return XST_FAILURE;
	}

	return XST_SUCCESS;
}

/*****************************************************************************/
/**
* This function updates the current state while downloading a file
*
* @param	DFU is a pointer to DFU instance of the controller
* @param	status is a pointer of the DFU status
*
* @note		None.
*
******************************************************************************/
void USB_DfuSetDwloadState(struct dfu_if *DFU, u8 *status)
{

	if (DFU->got_dnload_rqst != 1) {
		return;
	}

	switch (DFU->curr_state) {

		case STATE_DFU_IDLE:
			DFU->curr_state = STATE_DFU_DOWNLOAD_SYNC;
			break;

		case STATE_DFU_DOWNLOAD_SYNC:
			DFU->curr_state = STATE_DFU_DOWNLOAD_BUSY;
			break;

		default:
			break;
	}
}

/*****************************************************************************/
/**
* This function handles getting of DFU status.
*
* @param	DFU is a pointer to DFU instance of the controller
* @param	status is the pointer of the DFU status
*
* @return	None
*
* @note		None.
*
******************************************************************************/
void USB_DfuGetStatus(struct dfu_if *DFU, u8 *status)
{
	/* update the download state */
	USB_DfuSetDwloadState(DFU, status);

	/* DFU status */
	status[0] = (u8)DFU->status;
	/* timeout to wait until next request */
	status[1] = (u8)(0);
	status[2] = (u8)0;
	status[3] = (u8)0;
	/* DFU current state */
	status[4] = (u8)DFU->curr_state;
	status[5] = (u8)0;
}

/*****************************************************************************/
/**
* This function handles USB disconnect, called from driver.
*
* @param	InstancePtr is a pointer to Usb_DevData instance of the controller
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void Usb_DisconnectHandler(struct Usb_DevData *InstancePtr)
{
	Xil_AssertVoid(InstancePtr != NULL);
	USBCH9_DATA *ch9_ptr =
		(USBCH9_DATA *)Get_DrvData(InstancePtr->PrivateData);
	struct dfu_if *DFU = (struct dfu_if *)(ch9_ptr->data_ptr);

	if (DFU->dfu_wait_for_interrupt == 1) {
		/* Tell DFU that we got disconnected */
		DFU->dfu_wait_for_interrupt = 0;
	}
	if (DFU->is_dfu == 1) {
		/* Switch to run time mode */
		DFU->is_dfu = 0;
	}
}

/*****************************************************************************/
/**
* This function handles USB reset, called from driver.
*
* @param	InstancePtr is a pointer to Usb_DevData instance of the controller
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void Usb_ResetHandler(struct Usb_DevData *InstancePtr)
{
	Xil_AssertVoid(InstancePtr != NULL);

	USBCH9_DATA *ch9_ptr =
		(USBCH9_DATA *)Get_DrvData(InstancePtr->PrivateData);
	struct dfu_if *DFU = (struct dfu_if *)(ch9_ptr->data_ptr);

	if (DFU->dfu_wait_for_interrupt == 1) {
		/* Tell DFU that we got reset signal */
		DFU->dfu_wait_for_interrupt = 0;
	}
}

/*****************************************************************************/
/**
* This function handles set interface request.
*
* @param	InstancePtr is a pointer to Usb_DevData instance of the controller
* @param	SetupData is a pointer to setup token of control transfer
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void Usb_SetIntf(struct Usb_DevData *InstancePtr,  SetupPacket *SetupData)
{
	Xil_AssertVoid(InstancePtr != NULL);

	s32 RetVal;
	u16 MaxPktSize = 1024;
	USBCH9_DATA *ch9_ptr =
		(USBCH9_DATA *)Get_DrvData(InstancePtr->PrivateData);
	struct audio_dfu_if *interface = (struct audio_dfu_if *)(ch9_ptr->data_ptr);
	struct audio_if *f_audio = &interface->f_audio;
	struct dfu_if *f_dfu = &interface->f_dfu;

	switch (SetupData->wIndex & 0xff) {

		case DFU_INTF:		/* DFU */
			/* Setting the alternate setting requested */
			f_dfu->current_inf = SetupData->wValue;
			if ((f_dfu->current_inf >= DFU_ALT_SETTING) ||
			    (f_dfu->runtime_to_dfu == 1)) {

				/* Clear the flag , before entering into
				 * DFU mode from runtime mode
				 */
				if (f_dfu->runtime_to_dfu == 1) {
					f_dfu->runtime_to_dfu = 0;
				}

				/* Entering DFU_IDLE state */
				Usb_DfuSetState(f_dfu, STATE_DFU_IDLE);
			} else {
				/* Entering APP_IDLE state */
				Usb_DfuSetState(f_dfu, STATE_APP_IDLE);
			}
			break;

		case AUDIO_INTF_IN:	/* AUDIO */
			if ((SetupData->wValue & 0xff) == 1) {

				f_audio->index = 0;
				f_audio->residue = 0;
				f_audio->firstpkt = 1;

				SetEpInterval(InstancePtr->PrivateData, ISO_EP,
					      USB_EP_DIR_IN, AUDIO_INTERVAL);

				/* Endpoint enables - not needed for Control EP */
				RetVal = EpEnable(InstancePtr->PrivateData, ISO_EP,
						  USB_EP_DIR_IN, MaxPktSize,
						  USB_EP_TYPE_ISOCHRONOUS);
				if (RetVal != XST_SUCCESS) {
					xil_printf("failed to enable ISOC IN Ep\r\n");
					return;
				}
			} else {

				StreamOff(InstancePtr->PrivateData, ISO_EP, USB_EP_DIR_IN);

				/* Endpoint disables - not needed for Control EP */
				RetVal = EpDisable(InstancePtr->PrivateData, ISO_EP,
						   USB_EP_DIR_IN);
				if (RetVal != XST_SUCCESS) {
					xil_printf("failed to disable ISOC IN Ep\r\n");
					return;
				}
			}

			break;

		case AUDIO_INTF_OUT:
			if ((SetupData->wValue & 0xff) == 1) {

				f_audio->index = 0;
				f_audio->residue = 0;
				f_audio->firstpkt = 1;

				SetEpInterval(InstancePtr->PrivateData, ISO_EP,
					      USB_EP_DIR_OUT, AUDIO_INTERVAL);

				/* Endpoint enables - not needed for Control EP */
				RetVal = EpEnable(InstancePtr->PrivateData, ISO_EP,
						  USB_EP_DIR_OUT, MaxPktSize,
						  USB_EP_TYPE_ISOCHRONOUS);
				if (RetVal != XST_SUCCESS) {
					xil_printf("failed to enable ISOC OUT Ep\r\n");
					return;
				}
			} else {

				StreamOff(InstancePtr->PrivateData, ISO_EP, USB_EP_DIR_OUT);

				/* Endpoint disables - not needed for Control EP */
				RetVal = EpDisable(InstancePtr->PrivateData, ISO_EP,
						   USB_EP_DIR_OUT);
				if (RetVal != XST_SUCCESS) {
					xil_printf("failed to disable ISOC OUT Ep\r\n");
					return;
				}
			}

			break;

		default:
			xil_printf("Invalid number for set interface request\r\n");
			break;
	}
}

/*****************************************************************************/
/**
* This function handles UAC1 class request.
*
* @param	InstancePtr is a pointer to Usb_DevData instance of the controller
* @param	SetupData is a pointer to setup token of control transfer
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void Usb_AudioClassReq(struct Usb_DevData *InstancePtr, SetupPacket *SetupData)
{
	u32 ReplyLen;
	u8 Error = 0;
#ifdef __ICCARM__
#if defined (PLATFORM_ZYNQMP) || defined (versal)
#pragma data_alignment = 64
#else
#pragma data_alignment = 32
#endif
	static u8 Reply[USB_REQ_REPLY_LEN];
#else
	static u8 Reply[USB_REQ_REPLY_LEN] ALIGNMENT_CACHELINE;
#endif
	u8 UnitId = SetupData->wIndex >> 8;

	/* Check that the requested reply length is not bigger than our reply
	 * buffer. This should never happen...
	 */
	if (SetupData->wLength > USB_REQ_REPLY_LEN) {
		return;
	}

	switch (SetupData->bRequest) {

		case UAC2_CS_CUR:
			switch (UnitId) {
				case USB_CLK_SRC_ID:
					switch (SetupData->wValue >> 8) {
						case UAC2_CS_CONTROL_SAM_FREQ:
							if ((SetupData->bRequestType &
							     USB_ENDPOINT_DIR_MASK) == 0) {
								/* Set Request */
								ReplyLen = SetupData->wLength;
								EpBufferRecv(InstancePtr->PrivateData, 0,
									     Reply, ReplyLen);

								EpBufferSend(InstancePtr->PrivateData, 0,
									     NULL, 0);
							} else {
								/* Get Request */
								ReplyLen = SetupData->wLength > 4 ? 4 :
									   SetupData->wLength;

								Reply[0] = (u8)0x44;
								Reply[1] = (u8)0xAC;
								Reply[2] = (u8)0x00;
								Reply[3] = (u8)0x00;

								EpBufferSend(InstancePtr->PrivateData, 0,
									     Reply, ReplyLen);
							}
							break;

						case UAC2_CS_CONTROL_CLOCK_VALID:
							ReplyLen = SetupData->wLength > 4 ? 4 :
								   SetupData->wLength;
							/* Internal clock always valid */
							Reply[0] = (u8)0x01;

							EpBufferSend(InstancePtr->PrivateData, 0,
								     Reply, ReplyLen);
							break;

						default:
							/* Unknown Control Selector for Clock Unit */
							Error = 1;
							break;
					}
					break;

				case USB_CLK_SEL_ID:
					if ((SetupData->bRequestType & USB_ENDPOINT_DIR_MASK) == 0) {
						/* Set Request */
						ReplyLen = SetupData->wLength;
						EpBufferRecv(InstancePtr->PrivateData, 0,
							     Reply, ReplyLen);

						EpBufferSend(InstancePtr->PrivateData, 0, NULL, 0);
					} else {
						/* Get Request */
						ReplyLen = SetupData->wLength > 4 ? 4 :
							   SetupData->wLength;
						Reply[0] = (u8)0x01;

						EpBufferSend(InstancePtr->PrivateData, 0,
							     Reply, ReplyLen);
					}
					break;

				case OUT_FETR_UNT_ID:
				case IN_FETR_UNT_ID:
					switch (SetupData->wValue >> 8) {
						case UAC2_FU_VOLUME_CONTROL:
							/* Feature not available */
							if ((SetupData->bRequestType &
							     USB_ENDPOINT_DIR_MASK) == 0) {
								/* Set Request */
								ReplyLen = SetupData->wLength;
								EpBufferRecv(InstancePtr->PrivateData, 0,
									     Reply, ReplyLen);

								EpBufferSend(InstancePtr->PrivateData, 0,
									     NULL, 0);
							} else {
								/* Get Request */
								ReplyLen = SetupData->wLength > 4 ? 4 :
									   SetupData->wLength;
								Reply[0] = 0x00;
								Reply[1] = 0x00;

								EpBufferSend(InstancePtr->PrivateData, 0,
									     Reply, ReplyLen);
							}
							break;

						case UAC2_FU_MUTE_CONTROL:
							/* Feature not available */
							if ((SetupData->bRequestType &
							     USB_ENDPOINT_DIR_MASK) == 0) {
								/* Set Request */
								ReplyLen = SetupData->wLength;
								EpBufferRecv(InstancePtr->PrivateData, 0,
									     Reply, ReplyLen);

								EpBufferSend(InstancePtr->PrivateData, 0,
									     NULL, 0);
							} else {
								/* Get Request */
								ReplyLen = SetupData->wLength > 4 ? 4 :
									   SetupData->wLength;
								Reply[0] = 0x01;

								EpBufferSend(InstancePtr->PrivateData, 0,
									     Reply, ReplyLen);
							}
							break;

						default:
							/* Unknown Control Selector for Feature Unit */
							Error = 1;
							break;
					}
					break;

				default:
					/* Unknown unit ID */
					Error = 1;
					break;
			}
			break;

		case UAC2_CS_RANGE:
			switch (UnitId) {
				case USB_CLK_SRC_ID:
					switch (SetupData->wValue >> 8) {
						case UAC2_CS_CONTROL_SAM_FREQ:
							ReplyLen = SetupData->wLength > 14 ? 14 :
								   SetupData->wLength;
							Reply[0] = (u8)0x01;
							Reply[1] = (u8)0x00;
							Reply[2] = (u8)0x44;
							Reply[3] = (u8)0xAC;
							Reply[4] = (u8)0x00;
							Reply[5] = (u8)0x00;
							Reply[6] = (u8)0x44;
							Reply[7] = (u8)0xAC;
							Reply[8] = (u8)0x00;
							Reply[9] = (u8)0x00;
							Reply[10] = (u8)0x00;
							Reply[11] = (u8)0x00;
							Reply[12] = (u8)0x00;
							Reply[13] = (u8)0x00;

							EpBufferSend(InstancePtr->PrivateData, 0,
								     Reply, ReplyLen);
							break;

						default:
							/* Unknown Clock Source Range Request */
							Error = 1;
							break;
					}
					break;

				case OUT_FETR_UNT_ID:
				case IN_FETR_UNT_ID:
					switch (SetupData->wValue >> 8) {
						case UAC2_FU_VOLUME_CONTROL:
							/* Feature not available */
							ReplyLen = SetupData->wLength > 14 ? 14 :
								   SetupData->wLength;
							Reply[0] = (u8)0x01;
							Reply[1] = (u8)0x00;
							Reply[2] = (u8)0x00;
							Reply[3] = (u8)0x81;
							Reply[4] = (u8)0x00;
							Reply[5] = (u8)0x00;
							Reply[6] = (u8)0x00;
							Reply[7] = (u8)0x01;

							EpBufferSend(InstancePtr->PrivateData, 0,
								     Reply, ReplyLen);
							break;

						default:
							/* Unknown Control Selector for Feature Unit */
							Error = 1;
							break;
					}
					break;

				default:
					/* Unknown unit ID */
					Error = 1;
					break;
			}

			break;
		default:
			Error = 1;
			Ep0StallRestart(InstancePtr->PrivateData);
			break;
	}

	/* Set the send stall bit if there is an error */
	if (Error) {
		Ep0StallRestart(InstancePtr->PrivateData);
	}
}

/*****************************************************************************/
/**
* This function handles DFU heart and soul of DFU state machine.
*
* @param	InstancePtr is a pointer to Usb_DevData instance of the controller
* @param	SetupData is a pointer to setup token of control transfer
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
static void Usb_DfuClassReq(struct Usb_DevData *InstancePtr, SetupPacket *SetupData)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(SetupData   != NULL);
	u32 txBytesLeft;
	u32 rxBytesLeft;
	s32 result = -1;

#ifdef __ICCARM__
#if defined (PLATFORM_ZYNQMP) || defined (versal)
#pragma data_alignment = 64
#else
#pragma data_alignment = 32
#endif
	static u8 DFUReply[6];
#else
	static u8 DFUReply[6] ALIGNMENT_CACHELINE;
#endif
	USBCH9_DATA *ch9_ptr =
		(USBCH9_DATA *)Get_DrvData(InstancePtr->PrivateData);
	struct audio_dfu_if *f = (struct audio_dfu_if *)(ch9_ptr->data_ptr);
	struct dfu_if *dfu = &(f->f_dfu);

	switch (SetupData->bRequest) {

		case DFU_DETACH:
#ifdef DFU_DEBUG
			xil_printf("Got DFU_DETACH\n");
#endif
			Usb_DfuSetState(dfu, STATE_APP_DETACH);
			break;

		case DFU_DNLOAD:
			if (dfu->got_dnload_rqst == 0) {
				dfu->got_dnload_rqst = 1;
			}

			if ((dfu->total_transfers == 0) && (SetupData->wValue == 0)) {
				/* we are the start of the data, clear
				 * the download counter
				 */
				dfu->total_bytes_dnloaded = 0;
			}

			rxBytesLeft = (SetupData->wLength);
			dfu->total_transfers++;

			if (rxBytesLeft > 0) {
				result = EpBufferRecv(InstancePtr->PrivateData, 0,
						      (f->virtualdisk) +
						      dfu->total_bytes_dnloaded,
						      rxBytesLeft);
				dfu->total_bytes_dnloaded += rxBytesLeft;
			} else if ((dfu->got_dnload_rqst == 1) && (rxBytesLeft == 0)) {
				dfu->curr_state = STATE_DFU_IDLE;
				dfu->got_dnload_rqst = 0;
				dfu->total_transfers = 0;
			}

			if ((dfu->got_dnload_rqst == 1) && (result == 0)) {
				dfu->curr_state = STATE_DFU_DOWNLOAD_IDLE;
				dfu->got_dnload_rqst = 0;
			}
			break;

		case DFU_UPLOAD:

			if ((dfu->total_transfers == 0) && (SetupData->wValue == 0)) {
				/* we are the start of the data, clear the
				 * download counter
				 */
				dfu->total_bytes_uploaded = 0;
			}

			txBytesLeft = (SetupData->wLength);
			dfu->total_transfers++;

			if (dfu->total_bytes_uploaded < dfu->total_bytes_dnloaded) {
				if ((dfu->total_bytes_uploaded + txBytesLeft) >
				    dfu->total_bytes_dnloaded) {
					/* Upload only remaining bytes */
					txBytesLeft =
						(dfu->total_bytes_dnloaded -
						 dfu->total_bytes_uploaded);
				}
				result = EpBufferSend(InstancePtr->PrivateData, 0,
						      (f->virtualdisk) +
						      dfu->total_bytes_uploaded,
						      txBytesLeft);
				dfu->total_bytes_uploaded += txBytesLeft;
			} else {
				/* Send a short packet */
				result = EpBufferSend(InstancePtr->PrivateData, 0,
						      (f->virtualdisk) +
						      dfu->total_bytes_uploaded, 0);
				dfu->total_bytes_uploaded = 0;
				dfu->total_transfers = 0;
			}
			dfu->curr_state = STATE_DFU_IDLE;
			break;

		case DFU_GETSTATUS:
			USB_DfuGetStatus(dfu, (u8 *)DFUReply);
			EpBufferSend(InstancePtr->PrivateData,
				     0, DFUReply, SetupData->wLength);
			break;

		case DFU_CLRSTATUS:
			if (dfu->curr_state == STATE_DFU_ERROR) {
				EpClearStall(InstancePtr->PrivateData, 0, USB_EP_DIR_IN);
				Usb_DfuSetState(dfu, STATE_DFU_IDLE);
			}
			break;

		case DFU_GETSTATE:
			DFUReply[0] = (u8)dfu->curr_state;
			EpBufferSend(InstancePtr->PrivateData,
				     0, DFUReply, SetupData->wLength);
			break;

		case DFU_ABORT:
			/* Stop current download transfer */
			if (dfu->got_dnload_rqst == 1) {
				StopTransfer(InstancePtr->PrivateData, 0, USB_EP_DIR_OUT);
				Usb_DfuSetState(dfu, STATE_DFU_IDLE);
			}
		default:

			/* Unsupported command. Stall the end point. */
			dfu->curr_state = STATE_DFU_ERROR;
			EpSetStall(InstancePtr->PrivateData, 0, USB_EP_DIR_IN);
			break;
	}
}

void Usb_ClassReq(struct Usb_DevData *InstancePtr, SetupPacket *SetupData)
{
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(SetupData   != NULL);

	switch (SetupData->wIndex & 0xff) {
		case DFU_INTF:
			Usb_DfuClassReq(InstancePtr, SetupData);
			break;

		case AUDIO_INTF:
		case AUDIO_INTF_OUT:
		case AUDIO_INTF_IN:
			Usb_AudioClassReq(InstancePtr, SetupData);
			break;

		default:
			xil_printf("Invalid class request\r\n");
			break;
	}
}
